/*
 * Copyright (C) 2019-2021 Alibaba Group Holding Limited
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "aos/kernel.h"
#include "ulog/ulog.h"

#include "tca6424.h"
#include "aos/hal/i2c.h"
#include "hal_gpio.h"

#define MODULE_NAME "DRV_TCA6424"

/* TCA6424 REGISTERS and Addresses */
/* Command Byte */

#define INPUT_P0 0x00
#define INPUT_P1 0x01
#define INPUT_P2 0x02

#define OUTPUT_P0 0x04
#define OUTPUT_P1 0x05
#define OUTPUT_P2 0x06

#define POLINV_P0 0x08
#define POLINV_P1 0x09
#define POLINV_P2 0x0A

#define CONFIG_P0 0x0C
#define CONFIG_P1 0x0D
#define CONFIG_P2 0x0E

#define AUTOINCR_ON 0x80
#define AUTOINCR_OFF 0x00

#define TCA6424_PORT_NUM 24
#define TCA6424_SUPPORT_NUM_IN_I2C 2
#define TCA_GPIO_PORT_NUM 8

typedef enum
{
    TCA_OPT_CONFIG = 0,
    TCA_OPT_OUTPUT,
    TCA_OPT_INPUT,
    TCA_OPT_TOGGLE,
} en_tca_opt;

typedef struct
{
    uint8_t i2c_port;
    uint8_t init_flag;
    gpio_dev_t gpio_reset;
    gpio_dev_t gpio_int;
    gpio_config_t work_mode[TCA6424_PORT_NUM];
} dev_tca6426_info_t;

/*for now it just support 2 device, but it can support more*/
static dev_tca6426_info_t g_st_devtc6426[MAX_I2C_BUS_NUM][TCA6424_SUPPORT_NUM_IN_I2C];

int32_t tca6426_device_init(uint8_t gpio_reset, uint8_t gpio_int, uint8_t i2c_port, uint16_t dev_addr)
{
    int32_t ret = 0;
    uint8_t dev_index = 0;
    uint8_t i = 0;

    /*TODO: one i2c bus can have 2 devices, we need to distinguish the i2c bus*/
    if (((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH)) || (i2c_port >= MAX_I2C_BUS_NUM))
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x", i2c_port, dev_addr);
        return -1;
    }

    dev_index = dev_addr - TCA6424_I2C_ADDR_LOW;
    if (g_st_devtc6426[i2c_port][dev_index].init_flag)
    {
        LOGI(MODULE_NAME, "i2c %d Module 0x%x have already inited", i2c_port, dev_addr);
        return 0;
    }

    /*init reset and interrupt pin*/
    g_st_devtc6426[i2c_port][dev_index].gpio_reset.port = gpio_reset;
    g_st_devtc6426[i2c_port][dev_index].gpio_reset.config = OUTPUT_PUSH_PULL;

    ret = hal_gpio_init(&g_st_devtc6426[i2c_port][dev_index].gpio_reset);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d Module 0x%x Dereset pin %d init fail 0x%x",
             i2c_port, dev_addr, gpio_reset, ret);
        return -1;
    }

    /*config interrupt pin*/
    g_st_devtc6426[i2c_port][dev_index].gpio_int.port = gpio_int;
    g_st_devtc6426[i2c_port][dev_index].gpio_int.config = IRQ_MODE;

    ret = hal_gpio_init(&g_st_devtc6426[i2c_port][dev_index].gpio_int);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d Module 0x%x interrupt pin %d init fail 0x%x",
             i2c_port, dev_addr, gpio_int, ret);
        return -1;
    }

    /*enbale interrup both edge*/

    /*reset tca6426 device*/
    ret = hal_gpio_output_low(&g_st_devtc6426[i2c_port][dev_index].gpio_reset);
    if (ret)
    {
        LOGE(MODULE_NAME, "Dereset pin %d output low fail 0x%x", gpio_reset, ret);
        return -1;
    }

    aos_msleep(10);

    ret = hal_gpio_output_high(&g_st_devtc6426[i2c_port][dev_index].gpio_reset);
    if (ret)
    {
        LOGE(MODULE_NAME, "Dereset pin %d output high fail 0x%x", gpio_reset, ret);
        return -1;
    }

    /*all the pin default is input , get all the initial values*/
    for (i = 0; i < TCA6424_PORT_NUM; i++)
    {
        g_st_devtc6426[i2c_port][dev_index].work_mode[i] = INPUT_HIGH_IMPEDANCE;
    }
    /*we should get the value*/

    /*set the init flag*/
    g_st_devtc6426[i2c_port][dev_index].init_flag = 1;

    return 0;
}

int32_t tca6424_device_deinit(uint8_t gpio_reset, uint8_t gpio_int, uint8_t i2c_port, uint16_t dev_addr)
{
    int32_t ret = 0;
    uint8_t dev_index = 0;

    /*TODO: one i2c bus can have 2 devices, we need to distinguish the i2c bus*/
    if (((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH)) || (i2c_port >= MAX_I2C_BUS_NUM))
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x", i2c_port, dev_addr);
        return -1;
    }

    dev_index = dev_addr - TCA6424_I2C_ADDR_LOW;
    if (g_st_devtc6426[i2c_port][dev_index].init_flag == 0)
    {
        LOGI(MODULE_NAME, "i2c %d Module 0x%x haven't init yet", i2c_port, dev_addr);
        return 0;
    }

    ret = hal_gpio_output_low(&g_st_devtc6426[i2c_port][dev_index].gpio_reset);
    if (ret)
    {
        LOGE(MODULE_NAME, "Dereset pin %d pull low fail 0x%x", gpio_reset, ret);
    }

    ret = hal_gpio_finalize(&g_st_devtc6426[i2c_port][dev_index].gpio_reset);
    if (ret)
    {
        LOGE(MODULE_NAME, "finalize pin %d fail 0x%x", gpio_reset, ret);
    }

    ret = hal_gpio_finalize(&g_st_devtc6426[i2c_port][dev_index].gpio_int);
    if (ret)
    {
        LOGE(MODULE_NAME, "finalize pin %d fail 0x%x", gpio_reset, ret);
    }

    memset(&g_st_devtc6426[i2c_port][dev_index], 0, sizeof(dev_tca6426_info_t));

    return 0;
}

static int32_t tca_get_command_value_by_gpio_config(uint8_t *pcmd, uint8_t *pindex, uint8_t port, uint8_t opt)
{
    uint8_t cmd = 0;
    uint8_t value = 0;

    switch (port)
    {
    case TCA_PORT0_0:
    case TCA_PORT0_1:
    case TCA_PORT0_2:
    case TCA_PORT0_3:
    case TCA_PORT0_4:
    case TCA_PORT0_5:
    case TCA_PORT0_6:
    case TCA_PORT0_7:
        if (opt == TCA_OPT_CONFIG)
        {
            cmd = CONFIG_P0;
        }
        if (opt == TCA_OPT_OUTPUT)
        {
            cmd = OUTPUT_P0;
        }
        if (opt == TCA_OPT_INPUT)
        {
            cmd = INPUT_P0;
        }
        if (opt == TCA_OPT_TOGGLE)
        {
            cmd = POLINV_P0;
        }
        value = (port % TCA_GPIO_PORT_NUM);
        break;
    case TCA_PORT1_0:
    case TCA_PORT1_1:
    case TCA_PORT1_2:
    case TCA_PORT1_3:
    case TCA_PORT1_4:
    case TCA_PORT1_5:
    case TCA_PORT1_6:
    case TCA_PORT1_7:
        if (opt == TCA_OPT_CONFIG)
        {
            cmd = CONFIG_P1;
        }
        if (opt == TCA_OPT_OUTPUT)
        {
            cmd = OUTPUT_P1;
        }
        if (opt == TCA_OPT_INPUT)
        {
            cmd = INPUT_P1;
        }
        if (opt == TCA_OPT_TOGGLE)
        {
            cmd = POLINV_P1;
        }
        value = (port % TCA_GPIO_PORT_NUM);
        break;
    case TCA_PORT2_0:
    case TCA_PORT2_1:
    case TCA_PORT2_2:
    case TCA_PORT2_3:
    case TCA_PORT2_4:
    case TCA_PORT2_5:
    case TCA_PORT2_6:
    case TCA_PORT2_7:
        if (opt == TCA_OPT_CONFIG)
        {
            cmd = CONFIG_P2;
        }
        if (opt == TCA_OPT_OUTPUT)
        {
            cmd = OUTPUT_P2;
        }
        if (opt == TCA_OPT_INPUT)
        {
            cmd = INPUT_P2;
        }
        if (opt == TCA_OPT_TOGGLE)
        {
            cmd = POLINV_P2;
        }
        value = (port % TCA_GPIO_PORT_NUM);
        break;
    default:
        return -1;
    }
    *pcmd = cmd;
    *pindex = value;
    return 0;
}

static int32_t tca_device_gpio_init(drv_manufacturer_t *gpio_drv, gpio_config_t config)
{
    int32_t ret = 0;
    uint8_t data = 0;
    uint8_t cmd = 0;
    uint8_t index = 0;
    uint16_t dev_addr = 0;
    i2c_dev_t i2c_port = {0};

    if (NULL == gpio_drv)
    {
        LOGE(MODULE_NAME, "Invalid Input");
        return -1;
    }

    i2c_port.port = (uint8_t)(((uint32_t)(gpio_drv->base) & 0xFFFF0000) >> 16);
    dev_addr = ((uint32_t)(gpio_drv->base) & 0x0000FFFF);

    if (((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH))
        || (i2c_port.port >= MAX_I2C_BUS_NUM) )
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x at %s", i2c_port.port, dev_addr, __func__);
        return -1;
    }

    if ((config != OUTPUT_PUSH_PULL) && (config != INPUT_HIGH_IMPEDANCE) && (config != IRQ_MODE))
    {
        LOGE(MODULE_NAME, "unsupport gpio mode %d on base 0x%x pin %d", config, 
            (uint32_t)gpio_drv->base, gpio_drv->pin);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].init_flag == 0)
    {
        LOGE(MODULE_NAME, "%s i2c %d module %d haven't init yet", __func__, i2c_port.port, dev_addr);
        return -1;
    }

    ret = tca_get_command_value_by_gpio_config(&cmd, &index, gpio_drv->pin, TCA_OPT_CONFIG);
    if (ret)
    {
        LOGE(MODULE_NAME, "Fail to get cmd value by pin %d", gpio_drv->pin);
        return -1;
    }

    ret = hal_i2c_mem_read(&i2c_port, dev_addr, cmd, sizeof(cmd), &data, sizeof(data), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "Fail to get pin config value %d", ret);
        return -1;
    }

    if (config == OUTPUT_PUSH_PULL)
    {
        data &= ~(1 << index);
    }
    else if (config == INPUT_HIGH_IMPEDANCE || config == IRQ_MODE)
    {
        data |= (1 << index);
    }

    ret = hal_i2c_mem_write(&i2c_port, dev_addr, cmd, sizeof(cmd), &data, sizeof(data), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "GPIO config fail %d \r\n", ret);
        return -1;
    }

    g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin] = config;

    return 0;
}

int32_t tca6424_device_gpio_output_high(drv_manufacturer_t *gpio_drv)
{
    int32_t ret = 0;
    uint8_t cmd = 0;
    uint8_t index = 0;
    uint8_t cur_value = 0;
    uint16_t dev_addr = 0;
    i2c_dev_t i2c_port = {0};

    if (NULL == gpio_drv)
    {
        LOGE(MODULE_NAME, "%s invalid input", __func__);
        return -1;
    }

    i2c_port.port = (uint8_t)(((uint32_t)(gpio_drv->base) & 0xFFFF0000) >> 16);
    dev_addr = ((uint32_t)(gpio_drv->base) & 0x0000FFFF);

    if (((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH))
        || (i2c_port.port >= MAX_I2C_BUS_NUM) )
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x at %s", i2c_port.port, dev_addr, __func__);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].init_flag == 0)
    {
        LOGE(MODULE_NAME, "%s i2c %d module %d haven't init yet", __func__, i2c_port.port, dev_addr);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin] != OUTPUT_PUSH_PULL)
    {
        LOGE(MODULE_NAME, "i2c %d module %d pin %d workmode is %d , output high fail",
             i2c_port.port, dev_addr, gpio_drv->pin,
             g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin]);
        return -1;
    }

    ret = tca_get_command_value_by_gpio_config(&cmd, &index, gpio_drv->pin, TCA_OPT_OUTPUT);
    if (ret)
    {
        LOGE(MODULE_NAME, "Fail to get cmd value by pin %d at %s", gpio_drv->pin, __func__);
        return -1;
    }

    ret = hal_i2c_mem_read(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d module %d fail to get reg %d value",
             i2c_port.port, dev_addr, cmd);
        return -1;
    }
    /*set gpio high*/
    cur_value |= (1 << index);

    ret = hal_i2c_mem_write(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d module %d fail to set reg %d value 0x%x",
             i2c_port.port, dev_addr, cmd, cur_value);
        return -1;
    }

    return 0;
}

int32_t tca6424_device_gpio_output_low(drv_manufacturer_t *gpio_drv)
{
    int32_t ret = 0;
    uint8_t cmd = 0;
    uint8_t index = 0;
    uint8_t cur_value = 0;
    uint16_t dev_addr = 0;
    i2c_dev_t i2c_port = {0};

    if (NULL == gpio_drv)
    {
        LOGE(MODULE_NAME, "%s invalid input", __func__);
        return -1;
    }

    i2c_port.port = (uint8_t)(((uint32_t)(gpio_drv->base) & 0xFFFF0000) >> 16);
    dev_addr = ((uint32_t)(gpio_drv->base) & 0x0000FFFF);

    if (((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH))
        || (i2c_port.port >= MAX_I2C_BUS_NUM) )
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x at %s", i2c_port.port, dev_addr, __func__);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].init_flag == 0)
    {
        LOGE(MODULE_NAME, "%s i2c %d module %d haven't init yet", __func__, i2c_port.port, dev_addr);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin] != OUTPUT_PUSH_PULL)
    {
        LOGE(MODULE_NAME, "i2c %d module %d pin %d workmode is %d , output low fail",
             i2c_port.port, dev_addr, gpio_drv->pin,
             g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin]);
        return -1;
    }

    ret = tca_get_command_value_by_gpio_config(&cmd, &index, gpio_drv->pin, TCA_OPT_OUTPUT);
    if (ret)
    {
        LOGE(MODULE_NAME, "Fail to get cmd value by pin %d at %s", gpio_drv->pin, __func__);
        return -1;
    }

    ret = hal_i2c_mem_read(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d module %d fail to get reg %d value",
             i2c_port.port, dev_addr, cmd);
        return -1;
    }

    /*set gpio low*/
    cur_value &= ~(1 << index);

    ret = hal_i2c_mem_write(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d module %d fail to set reg %d value 0x%x",
             i2c_port.port, dev_addr, cmd, cur_value);
        return -1;
    }

    return 0;
}

int32_t tca6424_device_gpio_toggle(drv_manufacturer_t *gpio_drv)
{
    int32_t ret = 0;
    uint8_t cmd = 0;
    uint8_t index = 0;
    uint8_t cur_value = 0;
    uint16_t dev_addr = 0;
    i2c_dev_t i2c_port = {0};

    if (NULL == gpio_drv)
    {
        LOGE(MODULE_NAME, "%s invalid input", __func__);
        return -1;
    }

    i2c_port.port = (uint8_t)(((uint32_t)(gpio_drv->base) & 0xFFFF0000) >> 16);
    dev_addr = ((uint32_t)(gpio_drv->base) & 0x0000FFFF);

    if (((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH))
        || (i2c_port.port >= MAX_I2C_BUS_NUM) )
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x at %s", i2c_port.port, dev_addr, __func__);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].init_flag == 0)
    {
        LOGE(MODULE_NAME, "%s i2c %d module %d haven't init yet", __func__, i2c_port.port, dev_addr);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin] != INPUT_HIGH_IMPEDANCE)
    {
        LOGE(MODULE_NAME, "i2c %d module %d pin %d workmode is %d , toggle fail",
             i2c_port.port, dev_addr, gpio_drv->pin,
             g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin]);
        return -1;
    }

    ret = tca_get_command_value_by_gpio_config(&cmd, &index, gpio_drv->pin, TCA_OPT_TOGGLE);
    if (ret)
    {
        LOGE(MODULE_NAME, "Fail to get cmd value by pin %d at %s", gpio_drv->pin, __func__);
        return -1;
    }

    ret = hal_i2c_mem_read(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d module %d fail to get reg %d value",
             i2c_port.port, dev_addr, cmd);
        return -1;
    }

    /*set gpio toggle*/
    cur_value |= (1 << index);

    ret = hal_i2c_mem_write(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "i2c %d module %d fail to set reg %d value 0x%x",
             i2c_port.port, dev_addr, cmd, cur_value);
        return -1;
    }

    return 0;
}

int32_t tca6424_device_gpio_input_get(drv_manufacturer_t *gpio_drv, uint32_t *value)
{
    int32_t ret = 0;
    uint8_t cmd = 0;
    uint8_t index = 0;
    uint8_t cur_value = 0;
    uint16_t dev_addr = 0;
    i2c_dev_t i2c_port = {0};

    if (NULL == gpio_drv || NULL == value)
    {
        LOGE(MODULE_NAME, "%s invalid input", __func__);
        return -1;
    }

    i2c_port.port = (uint8_t)(((uint32_t)(gpio_drv->base) & 0xFFFF0000) >> 16);
    dev_addr = ((uint32_t)(gpio_drv->base) & 0x0000FFFF);

    if ((dev_addr != TCA6424_I2C_ADDR_LOW) && (dev_addr != TCA6424_I2C_ADDR_HIGH))
    {
        LOGE(MODULE_NAME, "Invalid I2C %d Dev Addr 0x%x at %s", i2c_port.port, dev_addr, __func__);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].init_flag == 0)
    {
        LOGE(MODULE_NAME, "%s i2c %d module %d haven't init yet", __func__, i2c_port.port, dev_addr);
        return -1;
    }

    if (g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin] == OUTPUT_PUSH_PULL)
    {
        LOGE(MODULE_NAME, "i2c %d module %d pin %d workmode is %d , get input fail",
             i2c_port.port, dev_addr, gpio_drv->pin,
             g_st_devtc6426[i2c_port.port][dev_addr - TCA6424_I2C_ADDR_LOW].work_mode[gpio_drv->pin]);
        return -1;
    }

    ret = tca_get_command_value_by_gpio_config(&cmd, &index, gpio_drv->pin, TCA_OPT_INPUT);
    if (ret)
    {
        LOGE(MODULE_NAME, "Fail to get cmd value by pin %d at %s", gpio_drv->pin, __func__);
        return -1;
    }

    ret = hal_i2c_mem_read(&i2c_port, dev_addr, cmd, sizeof(cmd), &cur_value, sizeof(cur_value), AOS_WAIT_FOREVER);
    if (ret)
    {
        LOGE(MODULE_NAME, "%s i2c %d module %d fail to get reg %d value",
             __func__, i2c_port.port, dev_addr, cmd);
        return -1;
    }

    if (cur_value & (1 << index))
    {
        *value = GPIO_PinState_Set;
    }
    else
    {
        *value = GPIO_PinState_Reset;
    }

    return 0;
}

int32_t tca_device_gpio_enable_irq(drv_manufacturer_t *gpio_drv, gpio_irq_trigger_t trigger,
                                   gpio_irq_handler_t handler, void *arg)
{
    LOGE(MODULE_NAME, "%s is stub here ", __func__);
    return -1;
}

int32_t tca_device_gpio_disable_irq(drv_manufacturer_t *gpio_drv)
{
    LOGE(MODULE_NAME, "%s is stub here ", __func__);
    return -1;
}

int32_t tca_device_gpio_clear_irq(drv_manufacturer_t *gpio_drv)
{
    LOGE(MODULE_NAME, "%s is stub here ", __func__);
    return -1;
}

static int32_t tca_device_gpio_finalize(drv_manufacturer_t *gpio_dr)
{
    LOGE(MODULE_NAME, "%s is stub here ", __func__);
    return -1;
}

const st_gpio_opt_t g_sttca6424_gpio_opt = {
    .init = tca_device_gpio_init,
    .finalize = tca_device_gpio_finalize,
    .output_high = tca6424_device_gpio_output_high,
    .output_low = tca6424_device_gpio_output_low,
    .output_toggle = tca6424_device_gpio_toggle,
    .input_get = tca6424_device_gpio_input_get,
    .enable_irq = tca_device_gpio_enable_irq,
    .disable_irq = tca_device_gpio_disable_irq,
    .clear_irq = tca_device_gpio_clear_irq,
};
